abstract class Shape {
    private id: string = '';
    protected type: string = '';
    abstract draw(): void;
    public getId(): string {
        return this.id;
    }
    public getType(): string {
        return this.type;
    }
    public setId(id: string): void {
        this.id = id;
    }
    public clone(deepCopy: boolean = false): Shape | null {
        let clone: Shape | null = null;
        try {
            if (deepCopy) {
                clone = JSON.parse(JSON.stringify(this));
            } else {
                clone = Object.create(this);
            }
        } catch (error: any) {
            console.log((error as Error).message);
        }
        return clone;
    }
}

class Rectangle extends Shape {
    public constructor() {
        super();
        this.type = 'Rectangle';
    }
    public override draw(): void {
        console.log('▲');
    }
}

class Circle extends Shape {
    public constructor() {
        super();
        this.type = 'Circle';
    }
    public override draw(): void {
        console.log('●');
    }
}

class Square extends Rectangle {
    public constructor() {
        super();
        this.type = 'Square';
    }
    public override draw(): void {
        console.log('■');
    }
}

class ShapeCache {
    private static shapeMap: Map<string, Shape> = new Map<string, Shape>();
    public static getShape(shapeId: string): Shape | null {
        const cachedShape: Shape | undefined = this.shapeMap.get(shapeId);
        if (cachedShape !== undefined) {
            return cachedShape.clone();
        } else {
            return null;
        }
    }
    public static loadCache(): void {
        const rectangle: Rectangle = new Rectangle();
        rectangle.setId('1');
        this.shapeMap.set(rectangle.getId(), rectangle);

        const circle: Circle = new Circle();
        circle.setId('2');
        this.shapeMap.set(circle.getId(), circle);

        const square: Square = new Square();
        square.setId('3');
        this.shapeMap.set(square.getId(), square);
    }
}

function main() {
    ShapeCache.loadCache();
    const clonedShape: Shape | null = ShapeCache.getShape('1');
    if (clonedShape !== null) {
        console.log('Shape: ' + clonedShape.getType());
    }

    const clonedShape2: Shape | null = ShapeCache.getShape('2');
    if (clonedShape2 !== null) {
        console.log('Shape: ' + clonedShape2.getType());
    }

    const clonedShape3: Shape | null = ShapeCache.getShape('3');
    if (clonedShape3 !== null) {
        console.log('Shape: ' + clonedShape3.getType());
    }
}

main();
